/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package zombiedefense.map;

import java.awt.Point;
import java.util.List;
import zombiedefense.framework.ZombieManager;
import zombiedefense.sprites.Zombie;
import zombiedefense.sprites.Zombie.ZombieType;
import zombiedefense.util.MathEx;
import zombiedefense.util.Timer;

/**
 * An object of this class handles
 * the spawning (creation) of zombie
 * sprites every wave.
 * 
 * Zombie Spawning works like this:
 * - A "spawn" event is triggered at a constant interval.
 * - Spawn events occur more frequently as the wave # increases.
 * - During a spawn event, a certain number of zombies are spawned.
 * - The number of zombies per spawn event increases as the wave # increases.
 * - Spawned zombies are placed randomly at any of the spawn points on the map.
 * - The type of zombie spawned is determined randomly.
 * - The probability of each zombie type also changes based on the wave #,
 * with more difficult zombies being spawned more frequently later.
 * - The total number of zombies in a wave is kept track of by the spawner. This value
 * also depends on the wave number.
 * - Some variable quatities used in spawning have maximum caps, such as the
 * max number of zombies allowed onscreen.
 * 
 * @author &{user}
 */
public class Spawner {
 
    private List<Point> spawnPoints;
    private ZombieManager zombies;
    
    private int totalCurrentWave;
    private int totalPerSpawn;
    private long waitBetweenSpawns;
    
    private static final long minWaitBetweenSpawns = 200;
    private static final int maxOnscreen = 300; //to minimize lag
    
    private int zombieCountdown;
    
    private Timer spawnTimer;
    
    public Spawner(ZombieManager zombies,TileMap map){
	//get spawnpoints from map
	spawnPoints = map.getSpawnPoints();
	this.zombies = zombies;
	
	spawnTimer = new Timer();
    }
    
    /**
     * This method creates zombies based on the current wave number,
     * and the behavior of the spawner object.
     * @param elapsedTime 
     */
    public void spawn(long elapsedTime){
	if(!spawnTimer.isStarted()){
	    spawnTimer.start();
	}
	
	spawnTimer.update(elapsedTime);
	
	//spawn once
	if(spawnTimer.hasTicked()){
	    createZombies();
	    spawnTimer.reset();
	}
    }
    
    private void createZombies(){    
	    int remaining = totalPerSpawn;
	    int point;
	    Tile t;
	    Point p;
	    
	    //due to rounding error, more zombies than the wave total may attempt creation. Also restrict amount onscreen.
	    while(remaining > 0 && zombieCountdown > 0 && zombies.getRemaining() < maxOnscreen){
		//get a random spawn point
		point = MathEx.getRandomInt(0,spawnPoints.size() - 1);
		p = spawnPoints.get(point);
		
		//add the zombie
		zombies.add(Zombie.createZombie(ZombieType.basicZombie,(double)p.x,(double)p.y));
		
		//update counters
		remaining--;
		zombieCountdown--;
	    }
    }
    
    private void calcValues(){
	int wave = zombies.getCurrentWave();
	
	//spawn-tuning functions
	totalCurrentWave = ((int)Math.pow(1.2,(double)wave)) + 5 * (wave - 1) + 4;
	totalPerSpawn = totalCurrentWave / wave;
	
	waitBetweenSpawns = 2/wave * 5000;
	waitBetweenSpawns = Math.max(waitBetweenSpawns,minWaitBetweenSpawns);
	spawnTimer.setInterval(waitBetweenSpawns);
    }
    
    /**
     * Informs the spawner that the wave
     * has advanced, forcing spawning calculations ahead.
     */
    public void waveHasChanged(){
	calcValues();
	zombieCountdown = totalCurrentWave;
    }
    
    /**
     * Gets the remaining zombies this wave.
     * @return 
     */
    public int getRemainingToSpawn(){
	return zombieCountdown;
    }
    
    public int getTotalForWave(){
	return totalCurrentWave;
    }
    
    /**
     * Paused/unpauses spawning.
     * @param p 
     */
    public void setPaused(boolean p){
	if(p){
	    spawnTimer.start();
	}else{
	    spawnTimer.stop();
	}
    }
}
